package com.laker.autotest.core;

import cn.hutool.core.text.CharSequenceUtil;
import com.laker.autotest.config.EasyConfig;
import com.laker.autotest.utils.PlaywrightUtil;
import com.laker.autotest.utils.TestNGUtil;
import com.microsoft.playwright.*;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.testng.ITestResult;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

/**
 * <p>
 * Playwright不是线程安全的，它所有方法以及创建的所有对象（例如 BrowserContext、Browser、Page 等）上的方法
 * 都应该在创建 Playwright 对象的同一线程上调用或进行适当的同步应该实现以确保在任何给定时间只有一个线程调用 Playwright 方法。
 * <p>
 * -- 注意：当前支持按照 classes级别并行执行。
 * </p>
 */
@Slf4j
public class BasePageTest {
    EasyConfig config = EasyConfigFactory.getConfig();
    // playwright 和 browser 在此类中的所有测试之间共享。
    private Playwright playwright;
    private Browser browser;
    // context 和 page 每个测试方法都是新实例。
    private BrowserContext context;
    @Getter
    protected Page page;

    @BeforeClass
    public void launchBrowser() {

        try {
            playwright = PlaywrightUtil.initPlaywright();
            BrowserType browserType = playwright.chromium();
            switch (config.getBrowser()) {
                case "chromium":
                    break;
                case "firefox":
                    browserType = playwright.firefox();
                    break;
                case "webkit":
                    browserType = playwright.webkit();
                    break;
                default:
                    throw new IllegalArgumentException("Unsupported browser type: " + config.getBrowser());
            }

            final BrowserType.LaunchOptions launchOptions = new BrowserType.LaunchOptions()
                    .setHeadless(config.getLaunchOptions().headless)
                    .setTimeout(config.getLaunchOptions().timeout)
                    .setSlowMo(config.getLaunchOptions().slowMo);
            if (CharSequenceUtil.isNotBlank(config.getLaunchOptions().getChannel())) {
                launchOptions.setChannel(config.getLaunchOptions().getChannel());
            }
            browser = browserType.launch(launchOptions);
        } catch (Exception e) {
            log.error("Browser is not installed or the version does not match!");
            throw e;
        }
        log("Browser launched");
    }


    @AfterClass
    public void closeBrowser() {
        try {
            if (browser != null) {
                browser.close();
            }
            if (playwright != null) {
                playwright.close();
            }
        } catch (Exception ignored) {
            // 忽略关闭异常
        }
        log("Browser closed");
    }

    @BeforeMethod
    public void createContextAndPage() {
        Browser.NewContextOptions newContextOptions = new Browser.NewContextOptions()
                .setViewportSize(config.getContextOptions().viewportWidth, config.getContextOptions().viewportHeight);
        if (config.getTraceOptions().videoEnable) {
            // 开启录制视频
            newContextOptions.setRecordVideoDir(Paths.get("videos/"))
                    // 指定视频大小
                    .setRecordVideoSize(640, 480);
        }
        // 加载cookie和localstorage, 模拟登录态
        if (config.getContextOptions().loginState) {
            newContextOptions.setStorageStatePath(Paths.get("auth.json"));
        }
        context = browser.newContext(newContextOptions);
        // 设置全局超时
        context.setDefaultTimeout(config.getContextOptions().timeout);
        if (config.getTraceOptions().traceEnable) {
            // Start tracing before creating / navigating a page.
            context.tracing().start(new Tracing.StartOptions()
                    .setScreenshots(true)
                    .setSnapshots(true));
        }
        page = context.newPage();
        log("Context and page created");
    }

    @AfterMethod
    public void closeContext(ITestResult result) {
        try {
            if (page != null) {
                page.close();
            }
            if (context != null) {
                trace(result);
                context.close();
                video(result);
            }
        } catch (Exception e) {
            log.error("closeContext error", e);
        }
        log("Context closed");
    }

    private void video(ITestResult result) throws IOException {
        if (config.getTraceOptions().videoEnable) {
            Path videoPath = page.video().path();
            if (result.getStatus() != ITestResult.FAILURE) {
                Files.deleteIfExists(videoPath);
            }

            if (result.getStatus() == ITestResult.FAILURE) {
                log.warn("{}-{} test failure!, video path: {}", ((BasePageTest) result.getInstance()).getClass()
                        .getSimpleName(), result.getName(), videoPath);
            }
        }
    }

    private void trace(ITestResult result) throws IOException {
        if (config.getTraceOptions().traceEnable) {
            String tracePath = TestNGUtil.getTracePath(result);
            Path path = Paths.get(tracePath);
            // Stop tracing and export it into a zip archive.
            context.tracing().stop(new Tracing.StopOptions()
                    .setPath(path));
            if (result.getStatus() != ITestResult.FAILURE) {
                Files.deleteIfExists(path);
            }
        }
    }

    private void log(String s) {
        log.debug("{} {} {}", this.getClass().getSimpleName(), this.hashCode(), s);
    }

}
